#!/usr/env/bin python
"""
Examples of concepts for pynchon.core.slice.
"""
import numpy as np
np.set_printoptions(precision=4, suppress=True, threshold=100, \
                    edgeitems=1, linewidth=120)
from pynchon.core.point import (Point0dFloat64, PointVector0dFloat64, \
                                PointArray0dFloat64, PointCube0dFloat64, \
                                PointHyperCube0dFloat64)
from pynchon.core.slice import (Slice0d, SliceVector0d, SliceArray0d, \
                                Slice2d, SliceVector2d, SliceArray2d, \
                                Slice3d, SliceVector3d, SliceArray3d)
# A Slice, SliceVector etc. is used to store and manipulate slices, slices are 
# used to slice entities, Python sequences and numpy arrays.
# need some random data
data = np.random.random(1000).reshape(10,10,10)
# create an entity, which is a (10,10,10) cube of floating point numbers. A good
# entity for floating of a cube of floating point numbers is 
# ``PointCube0dFloat64``.
data_entity = PointCube0dFloat64(data)
# create slice entities from Python slice objects. ``slice`` is a Python built-
# in type. Slices can be along a single or multiple dimensions here. 0d *and* 1d
# slices are equivalent to slice 1D arrays. 
slice0_entity = Slice0d(slice(0,10,1))
slice3_entity = Slice3d((slice(0,1), slice(0,1), slice(0,1)))
# a conise method to make tuple of slices is the numpy s_ slice trick
slice3_entity = Slice3d(np.s_[0:1,0:1,0:1])
# we can determine the indice tuple for a slice object using the indices method
slice0_entity_sss = slice0_entity.sss(100)
assert slice0_entity_sss.item() == (0,10,1)
# by setting the len as metadata of the Slice it is possible to omit the len 
# argument
slice0_entity.metadata['len'] = 100
slice0_entity_indices = slice0_entity.indices()
assert slice0_entity_indices.item() == set((0,1,2,3,4,5,6,7,8,9))
assert slice3_entity.sss((100,100,100)).item() == ((0, 1, 1), (0, 1, 1), (0, 1, 1))
## alternatively we can transformt the slice int sets(s) of indices
assert slice0_entity.indices(100).item() == set((0,1,2,3,4,5,6,7,8,9))
assert slice3_entity.indices((100,100,100)).item() == (set([0]), set([0]), set([0]))
## we slice the data array and any numpy array using the slice entity.
data_sliced0 = data_entity[slice0_entity.item()]
data_sliced3 = data_entity[slice3_entity.item()]
## the ranks of the results are the same
assert data_sliced0.rank == data_sliced3.rank == 3
## but data_sliced0 is a plane
assert data_sliced0.shape == (10,10,10)
## and data_sliced3 is a scalar
assert data_sliced3.shape == (1,1,1)
## we can also slice the data entity using it's slice method, which can be
## customized in sub-classes
data_sliced0 = data_entity.get_slices(slice0_entity)
data_sliced3 = data_entity.get_slices(slice3_entity)
## the effect is the same for rank 0 slice objects
## slices can be vectorized
slice0_01 = Slice0d(slice(0,1))
slice0_99 = Slice0d(slice(9,9))
slice_vector1 = SliceVector0d((slice0_01, slice0_99))
## a SliceVector is a custom ObjectVector
## it's element type is define in metadata
assert slice_vector1.metadata['element_type'] == slice
## a slice can be replaced
slice_vector1[1] = slice(1,2)
assert type(slice_vector1[1]) == slice
## we can slice the data_entity using a slice vector 
slices = data_entity.get_slices(slice_vector1)
## slices is by default a simple array of arrays
assert type(slices) is type(np.array(()))
assert slices.dtype == 'object'
## contents of the slices array are identical are 
## of the same type as the sliced Entity
assert isinstance(slices[0], PointCube0dFloat64)
assert isinstance(slices[1], PointCube0dFloat64)
## slice vectors can assembled into slice arrays
slice_vector2 = SliceVector0d((slice(7,10,1), slice(4,5,1), slice(9,15,1)))
assert slice_vector2.reduce_flat('union', 100) == set([4, 7, 8, 9, 10, 11, 12, 13, 14])
slice_array = SliceArray0d((slice_vector1, slice_vector2))
## this array has an element_type is slice
assert slice_array.metadata['element_type'] == slice
## but the input slices are of different length, as a consequence the
## slice array is padded with fval values, which are slice(0,0,0) objects, such
## a slice has the property that it cannot be used for slicing, because it is 
slices = data_entity.get_slices(slice_array)
### the shape of the slices is the same as the shape of the slice_array
assert slices.shape == slice_array.shape
### the elements corresponding to dummy slices are 
slice0_15 = Slice0d(slice(1,5))
slice0_29 = Slice0d(slice(2,9))
#assert slice0_15.union(10, slice0_29) == set([1, 2, 3, 4, 5, 6, 7, 8])
#assert slice0_15.intersection(10, slice0_29) == set([2, 3, 4])
### the same can be done with indices indexing multiple dimensions
slice2_15 = Slice2d((slice(1,5), slice(1,5)))
slice2_29 = Slice2d((slice(2,9), slice(2,9)))
### 
assert slice2_15.sss((100,100)).item() == ((1, 5, 1), (1, 5, 1))
assert slice2_15.indices((100,100)).item() == (set([1, 2, 3, 4]), set([1, 2, 3, 4]))
##
#assert slice2_15.union((100,100),slice2_29) == (set([1, 2, 3, 4, 5, 6, 7, 8]), \
#                                                set([1, 2, 3, 4, 5, 6, 7, 8]))
#assert slice2_15.intersection((100,100),slice2_29) == (set([2, 3, 4]), set([2, 3, 4]))
slice_vector3 = SliceVector2d((slice2_15, slice2_29))
slice_vector3_sss = slice_vector3.sss((100,100))
assert slice_vector3_sss.dtype == 'object'
assert slice_vector3_sss.shape == (2,)
assert slice_vector3_sss[0] == slice2_15.sss((100,100)).item()
slice_vector3_indices = slice_vector3.indices((100,100))
assert slice_vector3_indices.dtype == 'object'
assert slice_vector3_indices.shape == (2,)
assert slice_vector3_indices[0] == slice2_15.indices((100,100)).item()
assert slice_array.sss(100)[0][0] == (0, 1, 1)
## vectors of slices return arrays of indices
slice_vector4 = SliceVector0d((slice(2, 7, 1), slice(1, 5, 1), slice(3, 9, 1)))
assert slice_vector4.reduce_flat('union', 100) == set([1, 2, 3, 4, 5, 6, 7, 8])
assert slice_vector4.reduce_flat('intersection', 100) == set([3, 4])
assert slice_vector4.reduce_recursive(('union',), 100) == set([1, 2, 3, 4, 5, 6, 7, 8])
assert slice_vector4.reduce_recursive(('intersection',), 100) == set([3, 4])
slice_array4 = SliceArray0d((
                             (slice(1, 7, 1), slice(3,10, 1)),
                             (slice(1, 7, 1), slice(3,10, 1)),
                             ))
assert slice_array4.shape == (2,2)
assert slice_array4.rank == 2
a4sss = slice_array4.sss(100)
assert a4sss.shape == (2,2)
res = np.ndarray(shape=(2,2), dtype='object')
res[0][0] = (1, 7, 1)
res[0][1] = (3,10, 1)
res[1][0] = (1, 7, 1)
res[1][1] = (3,10, 1)
assert (a4sss == res).all()
b = slice_array4.indices(100)
assert b[0][0] == set(range(1, 7, 1))
assert b[0][1] == set(range(3,10, 1))
assert b[1][0] == set(range(1, 7, 1))
assert b[1][1] == set(range(3,10, 1))
assert slice_array4.reduce_flat('union', 100) == set([1, 2, 3, 4, 5, 6, 7, 8, 9])
assert slice_array4.reduce_flat('intersection', 100) == set([3, 4, 5, 6])
assert slice_array4.reduce_recursive(('union','union'), 100) == set([1, 2, 3, 4, 5, 6, 7, 8, 9])
assert slice_array4.reduce_recursive(('intersection','intersection'), 100) == set([3, 4, 5, 6])
assert slice_array4.reduce_recursive(('difference','intersection'), 100) == set([]) 
assert slice_array4.reduce_recursive(('union','intersection'), 100) == set([3,4,5,6])        
#assert slice_vector2.sss(100)[0] == (7, 10, 1)
slice_array4 = SliceArray0d((
                             (slice(1, 7, 1), slice(3,10, 1)),
                             (slice(1, 7, 1), slice(0,0, 0)),
                             ))
a4_sss = slice_array4.sss(100)
assert a4_sss[1][1] == None
a4_indices = slice_array4.indices(100)
assert a4_indices[1][1] == None
assert a4_indices[0][1] == set([3, 4, 5, 6, 7, 8, 9])

slice3_entity = Slice3d((slice(0,2,1), slice(0,3,1), slice(0,4,1)))
#assert slice3_entity.indices((100,100,100)) == (set([0, 1]), set([0, 1, 2]), \
#                                                set([0, 1, 2, 3]))
#print slice3_entity


## 

##print slice_array.union((100,100))

